
#include <BFont>
#include <BPoint>
#include <BPainter>
#include <BMouseEvent>
#include <BChangeEvent>
#include <BStyle>

#include <BTabWidget>
#include "member_BTabWidget.h"
#include "member_BAction.h"
#include "member_BWidget.h"

using namespace BWE;

#define member					(*(member_BTabWidget*)_ptr)
#define member_allocate()		_ptr = new member_BTabWidget(this)
#define member_release()		delete (member_BTabWidget*)_ptr

BTabWidget::BTabWidget(BWidget* parent) : BWidget(parent)
{
	member_allocate();
	this->setPerchs(0, member.tabSize.height(), 0, 0);
	this->setBorder(1);
	this->setMargins(1, 1, 1, 1);
}
BTabWidget::~BTabWidget()
{
	member_release();
}

void BTabWidget::setTabSize(const BSize& tabSize)
{
	if (member.tabSize != tabSize)
	{
		member.tabSize = tabSize;
		member.dirty = true;
		this->fresh();
	}
}
const BSize& BTabWidget::tabSize() const
{
	return member.tabSize;
}

void BTabWidget::setTabClosable(bool closable)
{
	if (member.tabClosable != closable)
	{
		member.tabClosable = closable;
		member.dirty = true;
		this->fresh();
	}
}
bool BTabWidget::tabClosable() const
{
	return member.tabClosable;
}

bool BTabWidget::addTab(BWidget* widget)
{
	if (!widget)
		return false;
	if (member.items.contain(widget))
		return true;
	this->addChild(widget);
	widget->hide();
	TabItem item;
	item.widget = widget;
	member.items.append(item);
	member.connect(widget, Signal_TitleChanged, &member_BTabWidget::procTitleChanged);
	if (member.stack.empty())
	{
		this->setCurrent(widget);
	}
	member.dirty = true;
	this->fresh();
	return true;
}
bool BTabWidget::removeTab(BWidget* widget)
{
	if (!widget)
		return false;
	if (!member.items.contain(widget))
		return false;
	member.disconnect(widget);
	member.stack.removeAll(widget);
	member.items.remove(widget);
	this->removeChild(widget);
	this->setCurrent(member.stack.last());
	member.dirty = true;
	this->fresh();
	return true;
}
bool BTabWidget::removeTab(int tab)
{
	BWidget* widget = member.items(tab).widget;
	return removeTab(widget);
}

bool BTabWidget::setCurrent(BWidget* widget)
{
	if (!widget)
		return false;
	if (this->current() == widget)
		return true;
	if (!member.items.contain(widget))
		return false;
	BWidget* current = member.stack.last();
	if (current)
		current->hide();
	widget->show();
	widget->setRect(this->clientRect());
	if (member.stack.last() != widget)
		member.stack.push(widget);
	member.reviseTab();
	emit(Signal_TabChanged, widget);
	this->fresh();
	return true;
}
bool BTabWidget::setCurrent(const BString& title)
{
	for (auto item : member.items)
	{
		if (item.widget->title() == title)
		{
			return setCurrent(item.widget);
		}
	}
	return false;
}
bool BTabWidget::setCurrent(int tab)
{
	BWidget* widget = member.items(tab).widget;
	return setCurrent(widget);
}
BWidget* BTabWidget::current() const
{
	return member.stack.last();
}

void BTabWidget::setBegin(int begin)
{
	member.begin = begin;
}
int BTabWidget::begin()
{
	return member.begin;
}

int BTabWidget::tabCount() const
{
	return member.items.size();
}
BWidget* BTabWidget::tab(int index)
{
	return member.items(index).widget;
}
const BWidget* BTabWidget::tab(int index) const
{
	return member.items(index).widget;
}

bool BTabWidget::contains(const BWidget* widget) const
{
	return member.items.contain(widget);
}
bool BTabWidget::contains(const BString& title) const
{
	for (const auto& item : member.items)
		if (item.widget->title() == title)
			return true;
	return false;
}

BWidget* BTabWidget::findTab(const BString& title)
{
	for (int i = 0; i < member.items.size(); i++)
	{
		const TabItem& item = member.items[i];
		if (item.widget->title() == title)
			return item.widget;
	}
	return 0;
}
const BWidget* BTabWidget::findTab(const BString& title) const
{
	for (int i = 0; i < member.items.size(); i++)
	{
		const TabItem& item = member.items[i];
		if (item.widget->name() == title)
			return item.widget;
	}
	return 0;
}

BRect BTabWidget::tabRect(int tab)
{
	member.freshRects();
	return member.items(tab).rect;
}
BRect BTabWidget::tabRect(BWidget* widget)
{
	member.freshRects();
	return member.rectOf(widget);
}

BSize BTabWidget::sizeHint() const
{
	int b = this->border();
	const BPerchs& p = this->perchs();
	const BMargins& m = this->margins();

	int width = 0;
	int height = 0;
	for (int i = 0; i < member.items.size(); i++)
	{
		BSize sizeHint = member.items[i].widget->sizeHint();
		width = bMax(width, sizeHint.width());
		height = bMax(height, sizeHint.height());
	}

	width += b + p.left() + m.left() + m.right() + p.right() + b;
	height += b + p.top() + m.top() + m.bottom() + p.bottom() + b;

	return BSize(width, height);
}

void BTabWidget::adjustEvent(const BEvent& event)
{
	BWidget* current = this->current();
	if (current)
	{
		current->setRect(this->clientRect());
	}
}
void BTabWidget::changeEvent(const BChangeEvent& event)
{
	if (event.change() == Change_Style)
	{
		const BStyle* style = event.style();

		BIcon* closeIcon = style->icon(Icon_Title_Button_Close);
		if (closeIcon)
			member.closeIcon = closeIcon;

		member.dirty = true;
	}
}
void BTabWidget::freshEvent(const BEvent& event)
{
	if (member.dirty)
	{
		member.freshRects();
		member.dirty = true;
	}
}
void BTabWidget::paintEvent(const BEvent& event)
{
	BPainter painter(this);
	painter.drawPerch();
	painter.drawMargin();

	for (int i = member.begin; i < member.items.size(); i++)
	{
		TabItem& item = member.items[i];

		BRect textRect = item.textRect.align(Align_VCenter, item.textSize);
		if (item.widget == member.stack.last())
		{
			painter.setColor(Color_Tab_Checked);
			painter.fillRect(item.rect);
			painter.setColor(Color_Checked);
			painter.setLineWidth(this->margins().top());
			painter.setLineSmooth(true);
			BPoint points[4];
			points[0] = item.rect.leftBottom();
			points[1] = item.rect.leftTop();
			points[2] = item.rect.rightTop();
			points[3] = item.rect.rightBottom();
			painter.drawLineStrip(points, 4);
			painter.setColor(Color_Text_Checked);
		}
		else if (i == member.hoverIndex)
		{
			painter.setColor(Color_Tab_Hovered);
			painter.fillRect(item.rect);
			painter.setColor(Color_Text_Hovered);
		}
		else
		{
			painter.setColor(Color_Text);
		}
		if (item.t_title.size())
			painter.drawText(textRect, item.t_title);
		else
			painter.drawText(textRect, item.widget->title());

		if (member.tabClosable && i == member.hoverIndex)
		{
			if (member.closeIcon)
			{
				BRect closeRect = item.closeRect * 0.6;
				painter.setColor(255, 255, 255);
				BImage* image = member.closeIcon->image();
				painter.drawImage(closeRect, image);
			}
			else
			{
				if (member.hoverClose)
					painter.setColor(Color_Title_Button_Hovered);
				else
					painter.setColor(Color_Title_Button);
				BRect closeRect = item.closeRect * 0.6;
				painter.setLineWidth(1);
				painter.fillRect(closeRect);

				BRect lineRect = closeRect * 0.6f;
				painter.setColor(200, 200, 200);
				painter.drawLine(lineRect.leftTop(), lineRect.rightBottom());
				painter.drawLine(lineRect.leftBottom(), lineRect.rightTop());
			}
		}
	}

	painter.drawBorder();
}

void BTabWidget::mouseEnterEvent(const BMouseEvent& mouseEvent)
{
	member.hoverIndex = -1;
	member.hoverClose = false;
}
void BTabWidget::mouseLeaveEvent(const BMouseEvent& mouseEvent)
{
	if (member.hoverIndex != -1)
	{
		member.hoverIndex = -1;
		member.hoverClose = false;
		this->fresh();
	}
}

void BTabWidget::mousePressEvent(const BMouseEvent& mouseEvent)
{
	if (member.hoverIndex >= 0)
	{
		setCurrent(member.hoverIndex);
		this->fresh();
	}
}
void BTabWidget::mouseReleaseEvent(const BMouseEvent& mouseEvent)
{
	if(member.tabClosable && member.hoverClose)
	{
		this->removeTab(member.hoverIndex);
		this->fresh();
	}
}
void BTabWidget::mouseMoveEvent(const BMouseEvent& mouseEvent)
{
	int hoverIndex = -1;
	bool hoverClose = false;
	const BPoint& pos = mouseEvent.pos();
	for(int i = member.begin; i < member.items.size(); i++)
	{
		TabItem& item = member.items[i];
		if(item.rect.contain(pos))
		{
			hoverIndex = i;
			hoverClose = item.closeRect.contain(pos);
			break;
		}
	}
	if (member.hoverIndex != hoverIndex || member.hoverClose != hoverClose)
	{
		member.hoverIndex = hoverIndex;
		member.hoverClose = hoverClose;
		this->fresh();
	}
}

